package x.ovo.wechat.bot.impl.http;

import lombok.Cleanup;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.dromara.hutool.json.JSONUtil;
import x.ovo.wechat.bot.core.Context;
import x.ovo.wechat.bot.core.exception.ApiExcption;
import x.ovo.wechat.bot.core.http.EngineConfig;
import x.ovo.wechat.bot.core.http.HttpEngine;
import x.ovo.wechat.bot.core.http.request.ApiRequest;

import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * 默认 HTTP 引擎，使用枚举实现单例模式
 *
 * @author ovo on 2024/07/08.
 */
@Slf4j(topic = "HttpEngine")
public enum DefaultHttpEngine implements HttpEngine<OkHttpClient, Request> {
    INSTANCE;

    /** http 客户端 */
    private OkHttpClient client;
    /** 默认cookie存储 */
    private DefaultCookieStore store;

    /**
     * 创建并初始化一个DefaultHttpEngine实例。
     * <p>
     * 此方法提供了一个单例模式的实现，确保整个应用程序中只存在一个DefaultHttpEngine实例。
     * 它通过传入的EngineConfig配置对象来初始化这个实例，包括设置Cookie存储和执行其他必要的初始化步骤。
     *
     * @param config EngineConfig对象，包含了初始化HttpEngine所需的配置信息。
     * @return 返回初始化后的DefaultHttpEngine实例。
     */
    public static DefaultHttpEngine create(EngineConfig config) {
        // 初始化Cookie存储，用于在HTTP请求/响应过程中存储和管理Cookie。
        INSTANCE.store = DefaultCookieStore.INSTANCE;
        // 使用传入的配置信息初始化HttpEngine实例。
        INSTANCE.init(config);
        // 返回初始化后的HttpEngine实例。
        return INSTANCE;
    }


    @Override
    public void init(@NonNull EngineConfig config) {
        if (Objects.nonNull(this.client)) return;

        // 初始化Cookie存储，用于在HTTP请求/响应过程中存储和管理Cookie。
        this.store = DefaultCookieStore.INSTANCE;
        OkHttpClient.Builder builder = new OkHttpClient.Builder();


        if (config.getConnectionTimeout() > 0) builder.connectTimeout(config.getConnectionTimeout(), TimeUnit.SECONDS);
        if (config.getReadTimeout() > 0) builder.readTimeout(config.getReadTimeout(), TimeUnit.SECONDS);
        if (config.getWriteTimeout() > 0) builder.writeTimeout(config.getWriteTimeout(), TimeUnit.SECONDS);
        // 默认关闭自动跳转
        builder.followRedirects(false);
        builder.cookieJar(this.store);
//        builder.hostnameVerifier((hostname, session) -> true);

        this.client = builder.build();
        Context.INSTANCE.setHttpEngine(this);
        log.info("Http 引擎初始化成功");
    }

    @Override
    public OkHttpClient getRawEngine() {
        return this.client;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <S, T> T execute(ApiRequest<T> request, Function<S, T> responseHandler, Class<?> handlerType) throws ApiExcption {
        try {
            Request httpRequest = this.convert(request);
            @Cleanup Response response = this.client.newCall(httpRequest).execute();
            if (response.isSuccessful()) {
                if (handlerType == String.class) return responseHandler.apply((S) response.body().string());
                if (handlerType == byte[].class) return responseHandler.apply((S) response.body().bytes());
                if (handlerType == InputStream.class) return responseHandler.apply((S) response.body().byteStream());
            }
            throw new ApiExcption("请求失败，HTTP状态码: " + response.code());
        } catch (IOException e) {
            throw new ApiExcption(e);
        }
    }

    @Override
    public Request convert(ApiRequest<?> request) {

        final Request.Builder builder = new Request.Builder().url(request.getUrl());

        // 为了兼容支持rest请求，在此不区分是否为GET等方法，一律按照body是否有值填充，兼容
        byte[] bytes = JSONUtil.toJsonStr(request.getParameters()).getBytes();
        builder.method(request.getMethod(), "GET".equals(request.getMethod()) ? null : RequestBody.create(bytes, MediaType.parse(request.getContentType())));
        builder.url(request.getUrl());
        // 填充头信息
        request.getHeaders().forEach(builder::addHeader);
        return builder.build();
    }
}
